using System;
using System.Collections;
using System.Collections.Generic;

namespace Bookvar.ApplicationModel
{
    /// <summary>
    /// This class represents a service container. It is a generic implementation of Dynamic Service Locator pattern.
    /// For more info see http://www.martinfowler.com/articles/injection.html#ADynamicServiceLocator.
    /// </summary>
    internal class BookvarServiceProvider : IBookvarServiceProvider
    {
        private readonly Hashtable services;
        private readonly Dictionary<Type, Dictionary<string, object>> servicesByName;

        /// <summary>
        /// Initializes a new instance of the <see cref="BookvarServiceProvider"/> class.
        /// </summary>
        public BookvarServiceProvider()
        {
            services = new Hashtable();
            servicesByName = new Dictionary<Type, Dictionary<string, object>>();
        }

        #region IBookvarServiceProvider Members

        /// <summary>
        /// Gets the service object of the specified type.
        /// </summary>
        /// <param name="serviceType">An object that specifies the type of service object to get.</param>
        /// <returns>
        /// A service object of type <paramref name="serviceType"/>.-or- null if there is no service object of type <paramref name="serviceType"/>.
        /// </returns>
        public object GetService( Type serviceType )
        {
            return services[ serviceType ];
        }

        /// <summary>
        /// Removes the service registered for the <paramref name="serviceType"/>.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        void IBookvarServiceProvider.RemoveService( Type serviceType )
        {
            RemoveService( serviceType );
        }

        /// <summary>
        /// Removes the service registered for the type of T.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        void IBookvarServiceProvider.RemoveService<T>()
        {
            RemoveService( typeof ( T ) );
        }

        /// <summary>
        /// Removes a service by its name. If there is no such service registered with this name, nothing happens.
        /// </summary>
        /// <typeparam name="T">The type of the service.</typeparam>
        /// <param name="name">The name of the service to remove.
        /// This name should be the same as the one with which the service has been registered.</param>
        public void RemoveService<T>( string name ) where T : class
        {
            if ( !servicesByName.ContainsKey( typeof ( T ) ) )
            {
                return;
            }

            servicesByName[ typeof ( T ) ].Remove( name );
        }

        void IBookvarServiceProvider.AddService( Type serviceType, object service )
        {
            AddService( serviceType, service );
        }

        /// <summary>
        /// Adds the service with the specified name.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="service">The service.</param>
        /// <param name="name">The name.</param>
        /// <exception cref="ArgumentNullException">if name or service is null.</exception>
        public void AddService<T>( T service, string name ) where T : class
        {
            if ( service == null ) throw new ArgumentNullException( "service" );
            if ( name == null ) throw new ArgumentNullException( "name" );

            if ( !servicesByName.ContainsKey( typeof ( T ) ) )
            {
                servicesByName[ typeof ( T ) ] = new Dictionary<string, object>();
            }

            servicesByName[ typeof ( T ) ].Add( name, service );
        }

        T IBookvarServiceProvider.GetService<T>()
        {
            return (T) GetService( typeof ( T ) );
        }

        public T GetService<T>( string name ) where T : class
        {
            if ( !servicesByName.ContainsKey( typeof ( T ) ) )
            {
                return null;
            }

            if ( servicesByName[ typeof ( T ) ] == null )
            {
                return null;
            }

            return (T) servicesByName[ typeof ( T ) ][ name ];
        }

        void IBookvarServiceProvider.AddService<T>( T service )
        {
            Type serviceType = typeof ( T );
            AddService( serviceType, service );
        }

        #endregion

        protected virtual void AddService( Type serviceType, object service )
        {
            services[ serviceType ] = service;
        }

        protected virtual void RemoveService( Type serviceType )
        {
            services.Remove( serviceType );
        }
    }
}
